﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Collections;
using System.Data;
using System.Reflection.Emit;
using System.Reflection;
using System.Data.Entity;

namespace AL.DAL
{

    // ★★ 使用方法 （注意：使用前，必须添加对程序集“EntityFramework, Version=4.1.0.0"的引用）

    //DALContext db = new DALContext();
    //var d = db.Database.SqlQueryForDynamic("SELECT UserName, UserNickName, UserName From Users");
    ////foreach (dynamic item in d){
    ////    var s = item.UserName;
    ////}
    //return View(d);

    // 页面显示代码 ( Razor )
    //@model System.Collections.IEnumerable
    //@{
    //    if (@Model != null)
    //    {
    //        foreach (dynamic item in @Model)
    //        {
    //         <ul>
    //            <li>@item.UserName </li>
    //            <li>@item.UserNickName </li>
    //            <li>@item.UserEmail </li>
    //         </ul>
    //        }
    //    }
    //}


    /// <summary>
    /// ★★★ HH: SqlQueryForDynamic的扩展方法（单独调用此方法不符合 UnitOfWork 模式！！请在 UnitOfWork 模式下的 DALContext 中调用此扩展方法）
    /// </summary>
    public static class DatabaseExtensions
    {

        /// <summary>
        /// HH: 直接输入 SQL 语句或者存储过程 动态创建类并返回实例（单独调用此方法不符合 UnitOfWork 模式！！请在 UnitOfWork 模式下的 DALContext 中调用此扩展方法）
        /// </summary>
        /// <param name="db"></param>
        /// <param name="sql"></param>
        /// <param name="parameters"></param>
        /// <returns></returns>
        public static IEnumerable SqlQueryForDynamic(this Database db, string sql, params object[] parameters)
        {
            IDbConnection defaultConn = new System.Data.SqlClient.SqlConnection();

            return SqlQueryForDynamicOtherDB(db, sql, defaultConn, parameters);
        }


        /// <summary>
        /// HH: 直接输入 SQL 语句或者存储过程 动态创建类并返回实例
        /// </summary>
        /// <param name="db"></param>
        /// <param name="sql"></param>
        /// <param name="conn"></param>
        /// <param name="parameters"></param>
        /// <returns></returns>
        public static IEnumerable SqlQueryForDynamicOtherDB(this Database db, string sql, IDbConnection conn, params object[] parameters)
        {
            //conn.ConnectionString = System.Web.Configuration.WebConfigurationManager.ConnectionStrings["DALContext"].ConnectionString;
            conn.ConnectionString = db.Connection.ConnectionString;

            if (conn.State != ConnectionState.Open)
            {
                conn.Open();
            }

            IDbCommand cmd = conn.CreateCommand();
            cmd.CommandText = sql;

            IDataReader dataReader = cmd.ExecuteReader();

            if (!dataReader.Read())
            {
                return null; //无结果返回Null
            }

            #region 构建动态字段

            TypeBuilder builder = DatabaseExtensions.CreateTypeBuilder(
                          "EF_DynamicModelAssembly",
                          "DynamicModule",
                          "DynamicType");

            int fieldCount = dataReader.FieldCount;
            for (int i = 0; i < fieldCount; i++)
            {
                ////dic.Add(i, dataReader.GetName(i));

                ////Type type = dataReader.GetFieldType(i);

                //DatabaseExtensions.CreateAutoImplementedProperty(
                //  builder,
                //  dataReader.GetName(i),
                //  dataReader.GetFieldType(i));


                Type type = dataReader.GetFieldType(i);
                if (type.FullName == "System.Int32") {
                    type = typeof(Nullable<Int32>);
                } else if (type.FullName == "System.Int16") {
                    type = typeof(Nullable<Int16>);            
                } else if (type.FullName == "System.DateTime") {
                    type = typeof(Nullable<DateTime>);
                }
                
                DatabaseExtensions.CreateAutoImplementedProperty(
                  builder,
                  dataReader.GetName(i),
                  type);
            }

            #endregion

            dataReader.Close();
            dataReader.Dispose();
            cmd.Dispose();
            conn.Close();
            conn.Dispose();

            Type returnType = builder.CreateType();

            if (parameters != null)
            {
                return db.SqlQuery(returnType, sql, parameters);
            }
            else
            {
                return db.SqlQuery(returnType, sql);
            }
        }


        /// <summary>
        /// HH: 运行时定义并创建类的新实例
        /// </summary>
        /// <param name="assemblyName"></param>
        /// <param name="moduleName"></param>
        /// <param name="typeName"></param>
        /// <returns></returns>
        public static TypeBuilder CreateTypeBuilder(string assemblyName, string moduleName, string typeName)
        {
            TypeBuilder typeBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(
              new AssemblyName(assemblyName),
              AssemblyBuilderAccess.Run).DefineDynamicModule(moduleName).DefineType(typeName,
              TypeAttributes.Public);
            typeBuilder.DefineDefaultConstructor(MethodAttributes.Public);
            return typeBuilder;
        }

        public static void CreateAutoImplementedProperty(TypeBuilder builder, string propertyName, Type propertyType)
        {
            const string PrivateFieldPrefix = "m_";
            const string GetterPrefix = "get_";
            const string SetterPrefix = "set_";

            // Generate the field.
            FieldBuilder fieldBuilder = builder.DefineField(
              string.Concat(PrivateFieldPrefix, propertyName),
              propertyType,
              FieldAttributes.Private);

            // Generate the property
            PropertyBuilder propertyBuilder = builder.DefineProperty(
              propertyName,
              System.Reflection.PropertyAttributes.HasDefault,
              propertyType, null);

            // Property getter and setter attributes.
            MethodAttributes propertyMethodAttributes = MethodAttributes.Public
              | MethodAttributes.SpecialName
              | MethodAttributes.HideBySig;

            // Define the getter method.
            MethodBuilder getterMethod = builder.DefineMethod(
                string.Concat(GetterPrefix, propertyName),
                propertyMethodAttributes,
                propertyType,
                Type.EmptyTypes);

            // Emit the IL code.
            // ldarg.0
            // ldfld,_field
            // ret
            ILGenerator getterILCode = getterMethod.GetILGenerator();
            getterILCode.Emit(OpCodes.Ldarg_0);
            getterILCode.Emit(OpCodes.Ldfld, fieldBuilder);
            getterILCode.Emit(OpCodes.Ret);

            // Define the setter method.
            MethodBuilder setterMethod = builder.DefineMethod(
              string.Concat(SetterPrefix, propertyName),
              propertyMethodAttributes,
              null,
              new Type[] { propertyType });

            // Emit the IL code.
            // ldarg.0
            // ldarg.1
            // stfld,_field
            // ret
            ILGenerator setterILCode = setterMethod.GetILGenerator();
            setterILCode.Emit(OpCodes.Ldarg_0);
            setterILCode.Emit(OpCodes.Ldarg_1);
            setterILCode.Emit(OpCodes.Stfld, fieldBuilder);
            setterILCode.Emit(OpCodes.Ret);

            propertyBuilder.SetGetMethod(getterMethod);
            propertyBuilder.SetSetMethod(setterMethod);
        }














        public static IEnumerable<TSource> DistinctBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
        {
            var seenKeys = new HashSet<TKey>();
            foreach (TSource element in source)
            {
                if (seenKeys.Add(keySelector(element)))
                {
                    yield return element;
                }
            }
        }







    }

}